package cmp.viewer.panels;
/**************************************************
 * Methods for displaying the table
 * Also see cmp.viewer.table
 * 	FieldData contains column names
 */
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.StringReader;
import java.sql.ResultSet;
import java.util.Enumeration;
import java.util.Vector;
import java.util.Calendar;
import java.text.SimpleDateFormat;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

import jpave.manager.ManagerFrame;

import util.Debug.ErrorReport;
import util.methods.BlastArgs;
import util.methods.UIHelpers;
import util.ui.UserPrompt;

import assem.DBConn;
import cmp.database.DBinfo;
import cmp.main.Globals;
import cmp.viewer.ViewerFrame;
import cmp.viewer.table.FieldData;
import cmp.viewer.table.SortTable;
import cmp.viewer.table.TableData;

public class ListDataPanel extends JPanel {
	private static final long serialVersionUID = -3827610586702639105L;

	public static boolean initFirstAppletPOGRun = true;
	public static boolean initFirstAppletUnitransRun = true;

	//Data/query modes
	public static final int MODE_POG = 0;
	public static final int MODE_POG_MEMBER = 1;
	public static final int MODE_UNITRANS = 2;

	//Debugging: allows all loaded columns to display (no sorting)
	private static final boolean SHOW_ALL_COLUMNS = false;
	private static final boolean SHOW_QUERY = false;
	
	//Widths for column selection
	private static final int COLUMN_SELECT_WIDTH = 75;
	private static final int COLUMN_PANEL_WIDTH = 900;

	public ListDataPanel(ViewerFrame parentFrame, String resultName, int queryMode) {
		setInitData(parentFrame, resultName, queryMode);	
		initialize(true); //Build the panel
	}
		
	//This constructor is used when showing a specific list of Unitrans, or POG IDs
	private ListDataPanel(ViewerFrame parentFrame, ListDataPanel parentList, 
			String resultName, int queryMode, String subQuery, String querySummary, int rowNum) {
		theParentList = parentList;
		nParentRow = rowNum;
		setInitData(parentFrame, resultName, queryMode);
		
		if(nQueryMode == MODE_POG) strTable = FieldData.POG_TABLE;
		else if(nQueryMode == MODE_POG_MEMBER) strTable = FieldData.MEMBER_TABLE;
		else if(nQueryMode == MODE_UNITRANS) strTable = FieldData.UNITRANS_TABLE;
		
		strQuerySummary = querySummary;
		strSubQuery = subQuery;
				
		initialize(false); //Build the panel
	}
	
	public void setInitData(ViewerFrame parentFrame, String resultName, int queryMode) {
		pFrame = parentFrame;
		nQueryMode = queryMode;
		strName = resultName;

		colSelectChange = new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				setTable(false);
				showTable();
				
				if(nQueryMode == MODE_POG)
					pFrame.getSettings().getPOGSettings().setSelectedColumns(getSelectedColumns());
				else if(nQueryMode == MODE_POG_MEMBER) 
					pFrame.getSettings().getMemberSettings().setSelectedColumns(getSelectedColumns());
				else if(nQueryMode == MODE_UNITRANS) 
					pFrame.getSettings().getUnitransSettings().setSelectedColumns(getSelectedColumns());
			}
		};
	}
		
	private void loadNewRow(int rowNum) {
		String [] strVals = theParentList.getUnitransQueryList(rowNum);
		nParentRow = rowNum;
		setInitData(pFrame, strVals[0], nQueryMode);
		
		if(nQueryMode == MODE_POG) strTable = FieldData.POG_TABLE;
		else if(nQueryMode == MODE_POG_MEMBER)  strTable = FieldData.MEMBER_TABLE;
		else if(nQueryMode == MODE_UNITRANS)  strTable = FieldData.UNITRANS_TABLE;
		
		strQuerySummary = strVals[1];
		strSubQuery = strVals[2];
					
		initialize(false); //Build the panel
	}
	
	//Set the appropriate query string, summary and primary table based on the mode
	private void setQueryData() {
		switch(nQueryMode) {
		case MODE_POG:
			strSubQuery = pFrame.getPOGSubQuery();
			strQuerySummary = pFrame.getPOGQuerySummary();
			strTable = FieldData.POG_TABLE;
			break;
		case MODE_UNITRANS:
			strSubQuery = pFrame.getUnitransSubQuery();
			strQuerySummary = pFrame.getUnitransQuerySummary();
			strTable = FieldData.UNITRANS_TABLE;
			break;
		}
	}
	
	public String getSummary() { return strQuerySummary; }
	public String getName() {
		if(theTable == null)
			return strName + " In progress"; 
		return strName; 
	}
	public void setName(String name) { strName = name; }
	public String getSubQuery() { return strSubQuery; }

	public void columnMoved() {
		showTable();
	}
	//When a column is selected/removed this is called to set the new model
	private void setTable(boolean loadMaster) {
		if(theTable != null) {
			//If this is not done, the old table stays in memory
			theTable.removeListeners();
		}
		
		//If debugging, display master table
		if(SHOW_ALL_COLUMNS)
			theTable = new SortTable(theTableData);
		else {
			if(!loadMaster) {
				//Sync the orders of the selected columns with the order in the table
				String [] columns = TableData.orderColumns(theTable, getSelectedColumns());
				//Copy the new model from the master table
				theTable = new SortTable(TableData.createModel(columns, theTableData, getInstance()));
			}
			else 
				theTable = new SortTable(TableData.createModel(getSelectedColumns(), theTableData, this));
		}
		
		//Set the attributes/interface
        theTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        theTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        theTable.autofitColumns();
        	
        theTable.getSelectionModel().addListSelectionListener(selListener);
		theTable.addSingleClickListener(sngClick);
        theTable.addDoubleClickListener(dblClick);

        theTable.setTableHeader(new SortHeader(theTable.getColumnModel()));
        
        //If a header contains a '\n' multiple lines will appear using this renderer
        MultiLineHeaderRenderer renderer = new MultiLineHeaderRenderer();
        Enumeration<TableColumn> en = theTable.getColumnModel().getColumns();
        while (en.hasMoreElements()) {
          ((TableColumn)en.nextElement()).setHeaderRenderer(renderer);
        } 
	}
	
	//Needed for setTable, cannot use 'this' in a thread
	private ListDataPanel getInstance() { return this; }
	
	//Common initialization for all constructors
	private void initialize(boolean isFilter) {
		//Set the query string and query summary for filters only (gets sent to QueryResultPanel)
		if(isFilter)
			setQueryData();

        setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
        setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
        setAlignmentY(LEFT_ALIGNMENT);

        //Set up the display for the user to see loading progress
        showProgress();
        //Has to be run on a thread 
		buildThread = new Thread(new Runnable() {
			public void run() {
				try {
					//Perform the query
					DBConn conn = pFrame.getDBConnection();

					ResultSet rset = ViewerFrame.executeQuery(conn, createQueryStr(), loadStatus);
					if(rset != null) {
						buildTable(rset);
						
						//Thread safe way of closing the resultSet
						ViewerFrame.closeResultSet(rset);
						conn.close();
						
						setTable(true);
					}
					//Update the interface with completed panel
					showTable();
					//Makes the table appear (A little hacky, but fast)
					if(isVisible()) {
						setVisible(false);
						setVisible(true);
					}
				} catch (Exception e) {
					ErrorReport.reportError(e, "Error generating list");
				} catch (Error e) {
					ErrorReport.reportFatalError(e, "Fatal error generating lisr", pFrame);
				}
			}
		});
		buildThread.setPriority(Thread.MIN_PRIORITY);
		buildThread.start();
	}
	
	/**
	 * @return number of rows in the master table
	 */
	public int getNumResults() {
		int retVal = 0;
		if(theTableData != null)
			retVal = theTableData.getNumRows();
		return retVal;
	}
	 
	//Perform the query
	private String createQueryStr() {
		FieldData theFields = null; 
		
		//Load DB fields based on query type, and set main table to read from
		if(nQueryMode == MODE_POG)
			theFields = FieldData.getPogFields(
					pFrame.getAsmList(), pFrame.getPOGLibList(), pFrame.getPOGDEList());
		else if(nQueryMode == MODE_POG_MEMBER) 
			theFields = FieldData.getMemberFields(
					pFrame.getSeqLibList(), pFrame.getSeqDEList(), pFrame.getMethodPrefixes());
		else if(nQueryMode == MODE_UNITRANS) 
			theFields = FieldData.getUnitransFields(
					pFrame.getSeqLibList(), pFrame.getSeqDEList(), pFrame.getMethodPrefixes());
		
        try {
	        	String strQuery = "SELECT " + theFields.getDBFieldQueryList() + " FROM " + strTable + " ";
	        	if(theFields.hasJoins())
	        		strQuery += theFields.getJoins();
	        	
	        	if(strSubQuery != null && strSubQuery.length() > 0)
	        		strQuery += " WHERE " + strSubQuery;
	        	
	        	//This is threaded.. check if the user canceled at some point, if so no data
	        	if(loadStatus.getText().equals("Cancelled")) return null; 
	        	
	        	//Notify the user
	        	loadStatus.setText("Getting data");
	        	
	        	if(SHOW_QUERY)
	        		System.out.println("Query: " + strQuery);
        		return strQuery;
        } catch(Exception e) {
	        	ErrorReport.reportError(e, "Error processing query");
	        	return null;
        } catch(Error e) {
	        	ErrorReport.reportFatalError(e, "Fatal error processing query", pFrame);
	        	return null;
        }
	}
	
	//Takes the query results and build the master table
    private void buildTable(ResultSet rset) {
    	//Load the database fields 
        FieldData theFields = null;
        if(nQueryMode == MODE_POG)
        		theFields = FieldData.getPogFields(pFrame.getAsmList(), pFrame.getPOGLibList(), pFrame.getPOGDEList());
        else if(nQueryMode == MODE_POG_MEMBER)
        		theFields = FieldData.getMemberFields(pFrame.getSeqLibList(), pFrame.getSeqDEList(), pFrame.getMethodPrefixes());
        else if(nQueryMode == MODE_UNITRANS)
        		theFields = FieldData.getUnitransFields(pFrame.getSeqLibList(), pFrame.getSeqDEList(),pFrame.getMethodPrefixes());
        
        if (nQueryMode == MODE_POG)
        		tableButtonPanel = createTableButtonPanelPOG();
        else
        		tableButtonPanel = createTableButtonPanelSeq();
        
		tableStatusPanel = createTableStatusPanel();	
		tableSummaryPanel = createTableSummaryPanel();
		fieldSelectPanel = createFieldSelectPanel();
		fieldSelectPanel.setVisible(false);
		
		//Create toggle for displaying column select
		showColumnSelect = new JButton("Select Columns");
		showColumnSelect.setBackground(Globals.MENUCOLOR);
		showColumnSelect.setAlignmentX(Component.LEFT_ALIGNMENT);
		
		showColumnSelect.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if(fieldSelectPanel.isVisible()) {
					showColumnSelect.setText("Select Columns");
					fieldSelectPanel.setVisible(false);
				}
				else {
					showColumnSelect.setText("Hide");
					fieldSelectPanel.setVisible(true);
				}
				//Panel is different now, refresh the panel
				showTable();
			}
		});
		
		txtStatus = new JTextField(100);
		txtStatus.setBorder(BorderFactory.createEmptyBorder());
		txtStatus.setEditable(false);
		txtStatus.setBackground(Globals.BGCOLOR);

		//Set action for table listeners
        dblClick = new ActionListener() {
			public void actionPerformed(ActionEvent e) {				
			}
		};
		
		sngClick = new ActionListener() {
			public void actionPerformed(ActionEvent e) {
			}
		};
		
		selListener = new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent arg0) {
				int rowCount = theTable.getSelectedRowCount();
				if(rowCount >= 1000)
					txtStatus.setText("Cannot select more than 1000 rows at a time");
				else
					txtStatus.setText("");
				
				if(btnShowUnitrans != null) {
					if (rowCount > 0)btnShowUnitrans.setEnabled(true);
					else btnShowUnitrans.setEnabled(false);
				}
				
				if(btnViewPairwiseAlignment != null) {
					if (rowCount != 1 && rowCount < 1000) btnViewPairwiseAlignment.setEnabled(true);
					else btnViewPairwiseAlignment.setEnabled(false);
				}
				
				if(btnViewMultiAlignment != null) {
					if (rowCount != 1 && rowCount < 1000) btnViewMultiAlignment.setEnabled(true);
					else btnViewMultiAlignment.setEnabled(false);
				}	
				
				if(btnBlast != null && rowCount == 1) btnBlast.setEnabled(true);
				else if(btnBlast != null) btnBlast.setEnabled(false);
				
				if(btnViewGroups != null && rowCount == 1) btnViewGroups.setEnabled(true);
				else if(btnViewGroups != null) btnViewGroups.setEnabled(false);
				
				if(btnViewDetails != null && rowCount == 1) btnViewDetails.setEnabled(true);
				else if(btnViewDetails != null) btnViewDetails.setEnabled(false);
				
				if(btnCopy != null && rowCount == 1) btnCopy.setEnabled(true);
				else if(btnCopy != null) btnCopy.setEnabled(false);
				updateButtonStatusLabels();
			}
		};

		//Create table data, file name is for caching (not implemented)
        theTableData = new TableData("hitList" + (nTableID++) + ".dat", this);
        	theTableData.setDEnames(pFrame.getSeqDEList(), pFrame.getPOGDEList());
        theTableData.setColumnHeaders(theFields.getDisplayFields(), theFields.getDisplayTypes());
        //Load data from query... any data processing needed i.e. calculated fields, should be done in there
        theTableData.addRowsWithProgress(rset, theFields, loadStatus);
        //Take dynamic structures and convert them to static arrays (increases speed/lowers memory usage)
        theTableData.finalize();

        String typeLabel = "";
        if(nQueryMode == MODE_POG)	typeLabel = "Cluster";
        else if(nQueryMode == MODE_POG_MEMBER) typeLabel = "Members";
        else if(nQueryMode == MODE_UNITRANS) typeLabel = "Sequence";
        
        //Update the menu and query results
        String status = "";
        if(theTableData.getNumRows() == 0)	status = "No " + typeLabel;
        else if(theTableData.getNumRows() == 1)	status = "1 " + typeLabel;
        else	 status = theTableData.getNumRows() + " " + typeLabel;
        
        if(theTableData.getNumRows() != 1 && nQueryMode != MODE_POG_MEMBER) {
        		status += "s";
        }
        
        if(!strName.equals(ViewerFrame.MAIN_MENU[2]) && !strName.equals(ViewerFrame.MAIN_MENU[3])) {
            rowCount.setText(strName + status);
            pFrame.changePanelName(this, strName + status, strQuerySummary);
        }
        else {
            rowCount.setText(status);
        }
    }
    
    //Returns string labels of each selected column (Always adding "Row" to position 0
    private String [] getSelectedColumns() {
	    	String [] retVal = null;
	    	
	    	int selectedCount = 0;
	    	for(int x=0; x<chkFields.length; x++) 
	    		if(chkFields[x].isSelected())
	    			selectedCount++;
	    	
	    	if(selectedCount == 0) {
	    		if(nQueryMode == MODE_POG) {
	    			for(int x=0; x<FieldData.POG_SELECTIONS.length; x++) {
	    				chkFields[x].setSelected(FieldData.POG_SELECTIONS[x]);
	    				if(chkFields[x].isSelected())
	    					selectedCount++;
	    			}
	    		}
	    		else if(nQueryMode == MODE_POG_MEMBER) {
	    			for(int x=0; x<FieldData.MEMBER_SELECTIONS.length; x++) {
	    				chkFields[x].setSelected(FieldData.MEMBER_SELECTIONS[x]);
	    				if(chkFields[x].isSelected())
	    					selectedCount++;
	    			}
	    		}
	    		else if(nQueryMode == MODE_UNITRANS) {
	    			for(int x=0; x<FieldData.UNITRANS_SELECTIONS.length; x++) {
	    				chkFields[x].setSelected(FieldData.UNITRANS_SELECTIONS[x]);
	    				if(chkFields[x].isSelected())
	    					selectedCount++;
	    			}
	    		}
	    	}
	    	retVal = new String[selectedCount];
	    	int targetIndex = 0;
	    	for(int x=0; x<chkFields.length; x++) 
	    		if(chkFields[x].isSelected()) {
	    			retVal[targetIndex] = chkFields[x].getText();
	    			targetIndex++;
	    		}
	    	
	    	return retVal;
    }
    
    //When the view table gets sorted, sort the master table to match (Called by TableData)
    public void sortMasterColumn(String columnName, boolean ascending) {
	    	int index = theTableData.getColumnHeaderIndex(columnName);
	    	theTableData.sortByColumn(index, ascending);
    }
    
    //Builds progress panel while data is being loaded
    private void showProgress() {
	    	removeAll();
	    	repaint();
	    	setBackground(Globals.BGCOLOR);
	    	loadStatus = new JTextField(40);
	    	loadStatus.setBackground(Globals.BGCOLOR);
	    	loadStatus.setMaximumSize(loadStatus.getPreferredSize());
	    	loadStatus.setEditable(false);
	    	loadStatus.setBorder(BorderFactory.createEmptyBorder());
	    	JButton btnStop = new JButton("Stop");
	    	btnStop.setBackground(Globals.BGCOLOR);
	    	btnStop.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				//This not only informs the user that it is canceled, 
				//but this is also the signal for the thread to stop. 
				if(buildThread != null)
					loadStatus.setText("Cancelled");
			}
		});
        add(loadStatus);
        add(btnStop);
        validateTable();
    }
    
    //Take built view table and build the panel
    private void showTable() {
	    	removeAll();
	    	repaint();
	    	loadStatus = null;
	    	sPane = new JScrollPane();
	    	sPane.setViewportView(theTable);
	    	theTable.getTableHeader().setBackground(Globals.BGCOLOR);
	    	sPane.setColumnHeaderView(theTable.getTableHeader());
	    	sPane.setAlignmentX(Component.LEFT_ALIGNMENT);
	    	
	    	sPane.getViewport().setBackground(Globals.BGCOLOR);
	    	sPane.getHorizontalScrollBar().setBackground(Globals.BGCOLOR);
	    	sPane.getVerticalScrollBar().setBackground(Globals.BGCOLOR);
	    	sPane.getHorizontalScrollBar().setForeground(Globals.BGCOLOR);
	    	sPane.getVerticalScrollBar().setForeground(Globals.BGCOLOR);
	    	
	    	//Is null if a sample table
	    	if(tableButtonPanel != null) {
	    		add(tableButtonPanel);
	    		add(Box.createVerticalStrut(10));
	    	}
	    	add(Box.createVerticalStrut(10));
	    	add(tableSummaryPanel);
	    	add(Box.createVerticalStrut(10));
	    	add(tableStatusPanel);
	    	add(sPane);
	    	add(fieldSelectPanel);
	    	add(Box.createVerticalStrut(10));
	    	JPanel temp = new JPanel();
	    	temp.setLayout(new BoxLayout(temp, BoxLayout.LINE_AXIS));
	    	temp.setAlignmentX(Component.LEFT_ALIGNMENT);
	    	temp.setBackground(Globals.BGCOLOR);
	    	temp.add(showColumnSelect);
	    	temp.add(Box.createHorizontalStrut(5));
	    	temp.add(txtStatus);
	    	temp.setMaximumSize(temp.getPreferredSize());
	    	
	    	add(temp);
	    	if(theTable != null)
				updateTableStatus();
	
	    	invalidate();
	    	validateTable();
        updateButtonStatusLabels();
    }
    
    private boolean [] getColumnSelections(String [] columns, 
    			boolean [] defaultSelections, String [] selections) {
    	
	    	if(selections == null || selections.length == 0)
	    		return defaultSelections;
	    	Vector<String> sels = new Vector<String> ();
	    	boolean [] retVal = new boolean[columns.length];
	    	for(int x=0; x<selections.length; x++)
	    		sels.add(selections[x]);
	    	for(int x=0; x<columns.length; x++) {
	    		retVal[x] = sels.contains(columns[x]);
	    	}
	    	return retVal;
    }
    
    //Create panel for selecting columns
    private JPanel createFieldSelectPanel() {
	    	JPanel retVal = new JPanel();
	    	retVal.setLayout(new BoxLayout(retVal, BoxLayout.PAGE_AXIS));
	    	retVal.setAlignmentX(Component.LEFT_ALIGNMENT);
	    	retVal.setBackground(Globals.BGCOLOR);
	    	
	    	String [] headers = null;
	    	int [] headerIdx = null;
	    	String [] columns = null;
	    	String [] descriptions = null;
	    	boolean [] selections = null;
	    	boolean [] defaults = null;
	    	
	    	//Load column names and default selections
	    	if(nQueryMode == MODE_POG) {
	    		String [] asm = pFrame.getAsmList();
	    		String [] rpkm = pFrame.getPOGLibList();
	    		String [] de = pFrame.getPOGDEList();
	    		
	    		headers = 		FieldData.getPOGColumnSections();
	    		headerIdx = 		FieldData.getPOGColumnSectionIdx(asm.length, rpkm.length, de.length);
	    		columns = 		FieldData.getPOGColumnSelect(asm, rpkm, de);
	    		descriptions = 	FieldData.getPOGDescriptionSelect(asm, rpkm, de);
	    		defaults = 		FieldData.getPOGSelections(asm.length, rpkm.length, de.length);
	    		
	    		if(pFrame.isApplet() && initFirstAppletPOGRun) {
	    			initFirstAppletPOGRun = false;
	    			selections = getColumnSelections(columns, defaults, null);
	    		}
	    		else
	    			selections = getColumnSelections(columns, defaults, 
	    							pFrame.getSettings().getPOGSettings().getSelectedColumns());
	    	}
	    	else if(nQueryMode == MODE_POG_MEMBER) {
	    		String [] lib = pFrame.getSeqLibList();
	    		String [] de  = pFrame.getSeqDEList();
	    		String [] met = pFrame.getMethodPrefixes();
	    		
	    		headers = 		FieldData.getMemberColumnSections();
	    		headerIdx = 		FieldData.getMemberColumnSectionIdx(lib.length, de.length, met.length); 
	    		columns = 		FieldData.getMemberColumnSelect(lib, de, met);
	    		descriptions = 	FieldData.getMemberDescriptionSelect(lib, de, met);
	    		defaults = 		FieldData.getMemberSelections(lib.length, de.length, met.length);
	    		
	    		if(pFrame.isApplet() && initFirstAppletUnitransRun) {
	    			initFirstAppletUnitransRun = false;
	    			selections = getColumnSelections(columns, defaults, null);
	    		}
	    		else
	    			selections = getColumnSelections(columns, defaults,
	    							pFrame.getSettings().getMemberSettings().getSelectedColumns());   		
	    	}
	    	else if(nQueryMode == MODE_UNITRANS) {
	    		String [] lib = pFrame.getSeqLibList();
	    		String [] de  = pFrame.getSeqDEList();
	    		String [] met = pFrame.getMethodPrefixes();
	    		
	    		headers = 		FieldData.getUnitransColumnSections();
	    		headerIdx =		FieldData.getUnitransColumnSectionIdx(lib.length, de.length, met.length);
	    		columns = 		FieldData.getUnitransColumnSelect(lib, de, met);
	    		descriptions = 	FieldData.getUnitransDescriptionSelect(lib, de, met);
	    		defaults = 		FieldData.getUnitransSelections(lib.length, de.length, met.length);
	    		
	    		if(pFrame.isApplet() && initFirstAppletUnitransRun) {
	    			initFirstAppletUnitransRun = false;
	    			selections = getColumnSelections(columns, defaults, null);
	    		}
	    		else
	    			selections = getColumnSelections(columns, defaults, 
	    					pFrame.getSettings().getUnitransSettings().getSelectedColumns());
	    	}
	    	
	    	chkFields = new JCheckBox[columns.length];
	
	    	JPanel row = new JPanel();
	    	row.setLayout(new BoxLayout(row, BoxLayout.LINE_AXIS));
	    	row.setAlignmentX(Component.LEFT_ALIGNMENT);
	    	row.setBackground(Globals.BGCOLOR);
			
	    	int rowWidth = 0;
	    	int hIdx = 0;
    	
		retVal.add(new JLabel(headers[0]));

	    	for(int x=0; x<columns.length; x++) {
	    		int newWidth = 0;
	    		int space = 0;
	    		final String desc = descriptions[x];
	    		
	        	chkFields[x] = new JCheckBox(columns[x]);
	        	chkFields[x].setBackground(Globals.BGCOLOR);
	        	chkFields[x].addActionListener(colSelectChange);
	        	chkFields[x].addMouseListener(new MouseAdapter() 
	        	{
	        		public void mouseEntered(MouseEvent e) {
	        			pFrame.setStatus(desc);
	        		}
	        		public void mouseExited(MouseEvent e) {
	        			pFrame.setStatus("");
	        		}
	        	});
	        	chkFields[x].setSelected(selections[x]);
	        	if(COLUMN_SELECT_WIDTH - chkFields[x].getPreferredSize().width > 0) {
	        		newWidth = COLUMN_SELECT_WIDTH;
	        		space = COLUMN_SELECT_WIDTH - chkFields[x].getPreferredSize().width;
	        	}
	        	else {
	        		space = 0;
	        		newWidth = chkFields[x].getPreferredSize().width;
	        	}
	        	
	        	if(rowWidth + newWidth >= COLUMN_PANEL_WIDTH || (hIdx < headerIdx.length && headerIdx[hIdx] == x)) {
	        		
	        		retVal.add(row);
	        		if(hIdx < headerIdx.length && headerIdx[hIdx] == x) {
	        			if(headers[hIdx+1].length() > 0) {
	        				retVal.add(Box.createVerticalStrut(10));
	        				retVal.add(new JLabel(headers[hIdx+1]));
	        			}
	        			hIdx++;
	        		}
	
	        		row = new JPanel();
	        		row.setLayout(new BoxLayout(row, BoxLayout.LINE_AXIS));
	            	row.setAlignmentX(Component.LEFT_ALIGNMENT);
	            	row.setBackground(Globals.BGCOLOR);
	        		
	            	rowWidth = 0;
	        	}
	        	
	    		row.add(chkFields[x]);
	    		row.add(Box.createHorizontalStrut(space));
	    		row.add(Box.createHorizontalStrut(10));
	    		
	    		rowWidth += newWidth + 10;
	    	}
	    	
	    	if(row.getComponentCount() > 0)
	    		retVal.add(row);
    	
	    	retVal.setBorder(BorderFactory.createTitledBorder("Columns"));
	    	retVal.setMaximumSize(retVal.getPreferredSize());
	
	    	return retVal;
    }
    
    //Called from a thread
    private void validateTable() {
    		validate();
    }
    
    private void updateButtonStatusLabels() {
	    	if(theTable == null) return;	//Catches events that happen before the table is built
	    	
	    	MouseAdapter [] oldListeners = null;
	    	String message = "";
        int [] sels = theTable.getSelectedRows();

        if(btnShowUnitrans != null) {
	    		oldListeners = btnShowUnitrans.getListeners(MouseAdapter.class);
	    		for(int x=0; x<oldListeners.length; x++)
	    			btnShowUnitrans.removeMouseListener(oldListeners[x]);
	    		
	    		if(sels.length > 0) {
			        message = "Find members for";
			        if(sels.length == theTable.getRowCount())
			     	   message += "(All Clusters)";
			        else {
			     	   message += "(" + sels.length + " Cluster";
			     	   if(sels.length > 1)
			     		   message += "s)";
			     	   else
			     		   message += ")";
			        }
	    		}
	    		else {
	    			message = "No Clusters selected";
	    		}
		    		
	    		btnShowUnitrans.addMouseListener(createStatusListener(message, ""));
        }
	        
	    	if(btnViewPairwiseAlignment != null) {
	    		oldListeners = btnViewPairwiseAlignment.getListeners(MouseAdapter.class);
	    		for(int x=0; x<oldListeners.length; x++)
	    			btnViewPairwiseAlignment.removeMouseListener(oldListeners[x]);
	    		
	    		if(sels.length != 1) {
			        message = "Execute pairwise alignment on selected Unitrans sequences ";
			        if(sels.length == 0 || sels.length == theTable.getRowCount())
			     	   message += "(All Unitrans)";
			        else
			     	   message += "(" + sels.length + " Unitrans)";
	    		}
	    		else {
	    			message = "Cannot perform pairwise alignment on one unitrans";
	    		}
	    		
		        btnViewPairwiseAlignment.addMouseListener(createStatusListener(message, ""));
	    	}
	   	
	    	if(btnViewMultiAlignment != null) {
	    		oldListeners = btnViewMultiAlignment.getListeners(MouseAdapter.class);
	    		for(int x=0; x<oldListeners.length; x++)
	    			btnViewMultiAlignment.removeMouseListener(oldListeners[x]);
	    		
	    		if(sels.length != 1) {
			        message = "Execute MUSCLE on selected Unitrans sequences ";
			        if(sels.length == 0 || sels.length == theTable.getRowCount())
			     	   message += "(All Unitrans)";
			        else
			     	   message += "(" + sels.length + " Unitrans)";
	    		}
	    		else {
	    			message = "Cannot perform MUSCLE alignment on one unitrans";
	    		}
		        btnViewMultiAlignment.addMouseListener(createStatusListener(message, ""));
	    	}
	   	
	    	if(btnImportMultiAlign != null) {
	    		oldListeners = btnImportMultiAlign.getListeners(MouseAdapter.class);
	    		for(int x=0; x<oldListeners.length; x++)
	    			btnImportMultiAlign.removeMouseListener(oldListeners[x]);

	    		if(sels.length != 1)
	    			btnImportMultiAlign.addMouseListener(createStatusListener("Load locally by cached MUSCLE result", ""));
	    	}
    }
    
    /** XXX Buttons for Cluster table */
    private JPanel createTableButtonPanelPOG() {
	    	JPanel topRow = new JPanel();
	    	topRow.setLayout(new BoxLayout(topRow, BoxLayout.LINE_AXIS));
	    	topRow.setBackground(Globals.BGCOLOR);
	
	    	JPanel buttonPanel = new JPanel();
	    	buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.PAGE_AXIS));
	    	buttonPanel.setBackground(Globals.BGCOLOR);

    	   	btnShowUnitrans = new JButton("View Selected Clusters");
        	btnShowUnitrans.setAlignmentX(Component.LEFT_ALIGNMENT);
        	btnShowUnitrans.setBackground(Globals.FUNCTIONCOLOR);
        	btnShowUnitrans.setEnabled(false);
        	btnShowUnitrans.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent arg0) {
					try{						
						String [] strVals = getUnitransQueryList();
						
						int row = -1;
						if(theTable.getSelectedRowCount() == 1)
							row = theTable.getSelectedRow();
						ListDataPanel newPanel = new ListDataPanel(pFrame, getInstance(), 
								strVals[0], MODE_POG_MEMBER, strVals[2], strVals[1], row);
						pFrame.addResultPanel(getInstance(), newPanel, newPanel.getName(), 
								newPanel.getSummary());
					
					} catch(Exception e) {
						ErrorReport.reportError(e, "Error saving file");
					} catch(Error e) {
						ErrorReport.reportFatalError(e, "Fatal error saving file", pFrame);
					}
				
				}
			});
        	topRow.add(btnShowUnitrans);
        	topRow.add(Box.createHorizontalStrut(5));        	   
         
        	 btnClearSelection = new JButton("Clear");
         btnClearSelection.setAlignmentX(Component.LEFT_ALIGNMENT);
         btnClearSelection.setBackground(Globals.BGCOLOR);
         btnClearSelection.addActionListener(new ActionListener() {
 			public void actionPerformed(ActionEvent arg0) {
 				theTable.clearSelection();
 			}
 		});
         btnClearSelection.addMouseListener(createStatusListener("Clear selected clusters", ""));
         // CAS 6July15 no need: topRow.add(btnClearSelection);
         // topRow.add(Box.createHorizontalStrut(5));
 		
         // Export
        final JPopupMenu popup = new JPopupMenu();
 		popup.add(new JMenuItem(new AbstractAction("Export table in tab-delimited format") {
 			private static final long serialVersionUID = 4692812516440639008L;
 			public void actionPerformed(ActionEvent e) {
 				exportTableTab("\t");
 			}
 		}));
 		// CAS 12/22/14 changed text to be clearer
 		popup.add(new JMenuItem(new AbstractAction("Export cluster members with library information") { 
 			private static final long serialVersionUID = 4692812516440639008L;
 			public void actionPerformed(ActionEvent e) {
 				exportClusterSeqTab();
 			}
 		}));
 		popup.add(new JMenuItem(new AbstractAction("Cancel") {
 			private static final long serialVersionUID = 4692812516440639008L;
 			public void actionPerformed(ActionEvent e) {
 				
 			}
 		}));
 		btnExport = new JButton("Export");
		btnExport.setBackground(ManagerFrame.PROMPTCOLOR);

		btnExport.addMouseListener(new MouseAdapter() {
	            public void mousePressed(MouseEvent e) {
	                popup.show(e.getComponent(), e.getX(), e.getY());
	            }
	        });
		
        topRow.add(btnExport);
        topRow.add(Box.createHorizontalStrut(5));
        
	    	btnCopyTable = new JButton("Copy Table");
	    	btnCopyTable.setAlignmentX(Component.LEFT_ALIGNMENT);
	    	btnCopyTable.setBackground(Globals.PROMPTCOLOR);
	    	btnCopyTable.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				Thread copyThread = new Thread(new Runnable() {
					public void run() {
						try {
							Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
							cb.setContents(new StringSelection(createTableString("\t")), null);
						} catch (Exception e) {							
							ErrorReport.reportError(e, "Error generating list");
						} catch (Error e) {
							ErrorReport.reportFatalError(e, "Fatal error generating lisr", pFrame);
						}
					}
				});
				copyThread.setPriority(Thread.MIN_PRIORITY);
				copyThread.start();	
			}
		});
		topRow.add(btnCopyTable);
		topRow.add(Box.createHorizontalStrut(5));
        
        btnHelp = new JButton("Help");
        btnHelp.setBackground(Globals.HELPCOLOR);
        btnHelp.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				UserPrompt.displayHTMLResourceHelp(pFrame, "Cluster table", "html/viewMultiTCW/ListDataPanel.html");
			}
		});
        topRow.add(btnHelp);

        topRow.setAlignmentX(LEFT_ALIGNMENT);
        topRow.setMaximumSize(topRow.getPreferredSize());

        buttonPanel.add(topRow);
        buttonPanel.add(Box.createVerticalStrut(5));
        
        return buttonPanel;
    }
  
    /* XXX Buttons for Member and Sequence table */
    private JPanel createTableButtonPanelSeq() {
	    	JPanel topRow = new JPanel();
	    	topRow.setLayout(new BoxLayout(topRow, BoxLayout.LINE_AXIS));
	    	topRow.setBackground(Globals.BGCOLOR);
	    	topRow.setAlignmentX(LEFT_ALIGNMENT);
	 	topRow.setMaximumSize(topRow.getPreferredSize());
	 	
	    	JPanel botRow = new JPanel();
	    	botRow.setLayout(new BoxLayout(botRow, BoxLayout.LINE_AXIS));
	    	botRow.setBackground(Globals.BGCOLOR);
	
	    	JPanel buttonPanel = new JPanel();
	    	buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.PAGE_AXIS));
	    	buttonPanel.setBackground(Globals.BGCOLOR);
	    	botRow.setAlignmentX(LEFT_ALIGNMENT);
	 	botRow.setMaximumSize(botRow.getPreferredSize());
	 	    
	    	btnViewPairwiseAlignment = new JButton("Pairwise Alignment");
         //btnViewPairwiseAlignment.setBackground(Globals.EXECCOLOR);
         btnViewPairwiseAlignment.setAlignmentX(RIGHT_ALIGNMENT);
         btnViewPairwiseAlignment.addActionListener(new ActionListener() {
         	public void actionPerformed(ActionEvent arg0) {
         		pFrame.addAlignmentTab(getInstance(), getSelectedPOGMembers(false), 
         				ViewerFrame.PAIR_MODE, strCacheFileName);
         	}
        });
        topRow.add(btnViewPairwiseAlignment);
        topRow.add(Box.createHorizontalStrut(3));
     
        if (!UIHelpers.isApplet()) {   
 	       btnViewMultiAlignment = new JButton("MUSCLE Alignment");
 	       //btnViewMultiAlignment.setBackground(Globals.EXECCOLOR);
 	       btnViewMultiAlignment.setAlignmentX(RIGHT_ALIGNMENT);
 	       btnViewMultiAlignment.addActionListener(new ActionListener() {
 	    	   	public void actionPerformed(ActionEvent arg0) {
 	    		   pFrame.addAlignmentTab(getInstance(), getSelectedPOGMembers(false), 
 	    				   ViewerFrame.MULTI_MODE, strCacheFileName);
 	    	   	}
 	       });
 	       	   
     	   topRow.add(btnViewMultiAlignment);
     	   topRow.add(Box.createHorizontalStrut(3));
        }
        
    	 // Export
        final JPopupMenu popup = new JPopupMenu();
 		popup.add(new JMenuItem(new AbstractAction("Export table in tab-delimited format") {
 			private static final long serialVersionUID = 4692812516440639008L;
 			public void actionPerformed(ActionEvent e) {
 				exportTableTab("\t");
 			}
 		}));
 		
 		popup.add(new JMenuItem(new AbstractAction("Cancel") {
 			private static final long serialVersionUID = 4692812516440639008L;
 			public void actionPerformed(ActionEvent e) {
 				
 			}
 		}));
 		btnExport = new JButton("Export");
		btnExport.setBackground(ManagerFrame.PROMPTCOLOR);

		btnExport.addMouseListener(new MouseAdapter() {
	            public void mousePressed(MouseEvent e) {
	                popup.show(e.getComponent(), e.getX(), e.getY());
	            }
	        });
		
		topRow.add(btnExport);
	    topRow.add(Box.createHorizontalStrut(3));
		    
	    btnCopyTable = new JButton("Copy Table");
	    btnCopyTable.setAlignmentX(Component.LEFT_ALIGNMENT);
    		btnCopyTable.setBackground(Globals.PROMPTCOLOR);
    		btnCopyTable.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				Thread copyThread = new Thread(new Runnable() {
					public void run() {
						try {											
								Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
								cb.setContents(new StringSelection(createTableString("\t")), null);
							} catch (Exception e) {							
							ErrorReport.reportError(e, "Error generating list");
						} catch (Error e) {
							ErrorReport.reportFatalError(e, "Fatal error generating lisr", pFrame);
						}
					}
				});
				copyThread.setPriority(Thread.MIN_PRIORITY);
				copyThread.start();
			}
    		});
		topRow.add(btnCopyTable);
	    topRow.add(Box.createHorizontalStrut(10));
	    
	    if(nParentRow >= 0) {
	 	   JPanel rowChangePanel = new JPanel();
	 	   rowChangePanel.setLayout(new BoxLayout(rowChangePanel, BoxLayout.LINE_AXIS));
	 	   rowChangePanel.setAlignmentX(Component.RIGHT_ALIGNMENT);
	 	   rowChangePanel.setBackground(pFrame.getSettings().getFrameSettings().getBGColor());
	 	   
	 	   btnPrevRow = new JButton("<< Prev");
	 	   btnPrevRow.setBackground(pFrame.getSettings().getFrameSettings().getBGColor());
	 	   btnPrevRow.addActionListener(new ActionListener() {
	 		   public void actionPerformed(ActionEvent arg0) {
	 			   nParentRow = theParentList.getTranslatedRow(nParentRow - 1);
	 			   loadNewRow(nParentRow);
	 		   }
	 	   });
	 	   btnPrevRow.addMouseListener(createStatusListener(theParentList.getName() + " row " + (theParentList.getTranslatedRow(nParentRow - 1) + 1), ""));
	 	   
	 	   btnNextRow = new JButton("Next >>");
	 	   btnNextRow.setBackground(pFrame.getSettings().getFrameSettings().getBGColor());
	 	   btnNextRow.addActionListener(new ActionListener() {
	 		   public void actionPerformed(ActionEvent arg0) {
	 			   nParentRow = theParentList.getTranslatedRow(nParentRow + 1);
	 			   loadNewRow(nParentRow);
	 		   }
	 	   });
	 	   btnNextRow.addMouseListener(createStatusListener(theParentList.getName() + " row " + (theParentList.getTranslatedRow(nParentRow + 1) + 1), ""));
	 	   
	 	   rowChangePanel.add(btnPrevRow);
	 	   //rowChangePanel.add(Box.createHorizontalStrut(1));
	 	   rowChangePanel.add(btnNextRow);
	 	   
	 	   topRow.add(rowChangePanel);
	    }

		//////// second row  
	    	
	    	 btnBlast = new JButton("Blast");
	     //btnBlast.setBackground(Globals.FUNCTIonCOLOR);
	     btnBlast.setEnabled(false);
	     btnBlast.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				if(theTable.getSelectedRowCount() != 1) { 
					JOptionPane.showMessageDialog(pFrame, "Select a Sequence");
				}
				else {
					int selectedRow = theTable.getSelectedRow();
		
					String name = (String)theTableData.getValueAt(selectedRow, theTableData.getColumnHeaderIndex("Seq ID"));
					String seq = (String)theTableData.getValueAt(selectedRow, theTableData.getColumnHeaderIndex("AASeq"));
					String [] options = {  "Tabular", "Long" };
					
					int selection = JOptionPane.showOptionDialog(pFrame, "Select output format" , "Blast " + name, 0, 
							JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
					
					if(selection == -1) return;
					boolean tabular = (selection == 0) ;
					runBlast(name, seq, tabular);
				}
			}
	     });
	     botRow.add(btnBlast);
	     botRow.add(Box.createHorizontalStrut(3));
		         
	    	 btnViewDetails = new JButton("View Details");
	     btnViewDetails.setBackground(Globals.FUNCTIONCOLOR);
	     btnViewDetails.setEnabled(false);
	     btnViewDetails.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				try {
					if(theTable.getSelectedRowCount() != 1) { 
						JOptionPane.showMessageDialog(pFrame, "Select a Sequence");
					}
					else {
						int selectedRow = theTable.getSelectedRow();
						long utid = (Long)theTableData.getValueAt(selectedRow, 
								theTableData.getColumnHeaderIndex("UTID"));
		
						SequenceViewPanel newPanel = new SequenceViewPanel(pFrame, strName, utid, selectedRow);
						pFrame.addResultPanel(getInstance(), newPanel, 
								newPanel.getName(), newPanel.getSummary());
					}
				}
				catch(Exception e) {
					ErrorReport.reportError(e, "Error viewing details");
				}
			}
	     });
	     botRow.add(btnViewDetails);
	     botRow.add(Box.createHorizontalStrut(3));
	
	     btnViewGroups = new JButton("View Clusters");
	     btnViewGroups.setBackground(Globals.FUNCTIONCOLOR);
	     btnViewGroups.setEnabled(false);
	     btnViewGroups.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				try{			
					if(theTable.getSelectedRowCount() != 1) { 
						JOptionPane.showMessageDialog(pFrame, "Select a Sequence");
					}
					else {
						int selectedRow = theTable.getSelectedRow();
						long utid = (Long)theTableData.getValueAt(selectedRow, theTableData.getColumnHeaderIndex("UTID"));
						String utstr = (String)theTableData.getValueAt(selectedRow, theTableData.getColumnHeaderIndex("Seq ID"));
						
						DBConn conn = pFrame.getDBConnection();
						ResultSet rset = conn.executeQuery("SELECT PGid FROM pog_members WHERE UTid=" + utid + " GROUP BY PGid");
						Vector<Long> ids = new Vector<Long> ();
						while(rset.next()) ids.add(rset.getLong(1));
						rset.close();
						String subQuery = getGroupQueryList(ids.toArray(new Long[0]));
			
						int row = -1;
						if(theTable.getSelectedRowCount() == 1)
							row = theTable.getSelectedRow();
						ListDataPanel newPanel = new ListDataPanel(pFrame, getInstance(), utstr + ": ", MODE_POG, subQuery, "Clusters for " + utstr, row);
						pFrame.addResultPanel(getInstance(), newPanel, newPanel.getName(), newPanel.getSummary());
		
						ids.clear();
					}
				} catch(Exception e) {
					ErrorReport.reportError(e, "Error viewing groups");
				} catch(Error e) {
					ErrorReport.reportFatalError(e, "Fatal error viewing groups", pFrame);
				}
			}
	     });
	     botRow.add(btnViewGroups);
	     botRow.add(Box.createHorizontalStrut(3));
	         
	 	btnCopy = new JButton("Copy");
	    btnCopy.setEnabled(false);
	    	btnCopy.setAlignmentX(Component.LEFT_ALIGNMENT);
	    	btnCopy.setBackground(Globals.PROMPTCOLOR);
	    final JPopupMenu copyPop = new JPopupMenu();
 		copyPop.add(new JMenuItem(new AbstractAction("Copy Seq ID") {
 			private static final long serialVersionUID = 4692812516440639008L;
 			public void actionPerformed(ActionEvent e) {
 				int row = theTable.getSelectedRow();
				Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
				cb.setContents(new StringSelection((String)theTableData.getValueAt(row, 
						theTableData.getColumnHeaderIndex("Seq ID"))), null);
 			}
 		}));
 		copyPop.add(new JMenuItem(new AbstractAction("Copy Sequence") {
 			private static final long serialVersionUID = 4692812516440639008L;
 			public void actionPerformed(ActionEvent e) {
				int row = theTable.getSelectedRow();
				Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
				cb.setContents(new StringSelection((String)theTableData.getValueAt(row, 
						theTableData.getColumnHeaderIndex("AASeq"))), null);
 			}
 		}));
 		copyPop.add(new JMenuItem(new AbstractAction("Cancel") {
 			private static final long serialVersionUID = 4692812516440639008L;
 			public void actionPerformed(ActionEvent e) {
 				
 			}
 		}));
	    	btnCopy.addMouseListener(new MouseAdapter() {
	            public void mousePressed(MouseEvent e) {
	                copyPop.show(e.getComponent(), e.getX(), e.getY());
	            }
	    });
		botRow.add(btnCopy);
	    botRow.add(Box.createHorizontalStrut(20));
		            
	    btnClearSelection = new JButton("Clear");
	    	btnClearSelection.setAlignmentX(Component.LEFT_ALIGNMENT);
	    btnClearSelection.setBackground(Globals.BGCOLOR);
	    btnClearSelection.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				theTable.clearSelection();
			}
		});
	    btnClearSelection.addMouseListener(createStatusListener("Clear selected items", ""));
	    botRow.add(btnClearSelection);
	    botRow.add(Box.createHorizontalStrut(3));
		    
	    btnHelp = new JButton("Help");
	    btnHelp.setBackground(Globals.HELPCOLOR);
	    btnHelp.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				UserPrompt.displayHTMLResourceHelp(pFrame, "Sequence table", "html/viewMultiTCW/ListDataPanelseq.html");
			}
		});
	    botRow.add(btnHelp);
		
	    topRow.setMaximumSize(topRow.getPreferredSize());
	    botRow.setMaximumSize(botRow.getPreferredSize());
	
	    buttonPanel.add(topRow);
	    buttonPanel.add(Box.createVerticalStrut(5));
	    buttonPanel.add(botRow);
	    buttonPanel.add(Box.createVerticalStrut(5));
	    
	    return buttonPanel;
	}
    private void runBlast(String name, String seq, boolean tabular) {
    		try {
    			boolean runFormat = false;
				//Get the working directory for the blast files
				File baseDir = new File("blast/");
				
				if(!baseDir.exists())
					baseDir.mkdir();
				
				File combinedFasta = new File(baseDir.getAbsolutePath() + "/Combined_" + pFrame.getDBName() + ".fasta");
				if(!combinedFasta.exists()) {
					if(!exportSequencesFasta(combinedFasta))
						return;
				}
										
				File phrCombined = (new File(combinedFasta.getAbsolutePath() + ".phr"));
				File pinCombined = (new File(combinedFasta.getAbsolutePath() + ".pin"));
				File psqCombined = (new File(combinedFasta.getAbsolutePath() + ".psq"));
									
				if(phrCombined.exists() && pinCombined.exists() && psqCombined.exists()) 
				{
					if((combinedFasta.lastModified() < phrCombined.lastModified()) &&
							(combinedFasta.lastModified() < pinCombined.lastModified()) &&
								(combinedFasta.lastModified() < psqCombined.lastModified())) 
					{
						runFormat = false;
					}
					else 
					{
						runFormat = true;
					}
				}
				else
				{
					runFormat = true;
				}

				if(runFormat) {
					if (!BlastArgs.isBlastPlus()) {
						System.err.println("Executing: " + "formatdb -i " + combinedFasta.getAbsolutePath());
						Process pFormatDB = Runtime.getRuntime().exec("formatdb -i " + combinedFasta.getAbsolutePath());
						pFormatDB.waitFor();
					}
					else {
						String cmd = BlastArgs.getFormatp(combinedFasta.getAbsolutePath());
						System.err.println("Executing: " + cmd);
						Process pFormatDB = Runtime.getRuntime().exec(cmd);
						pFormatDB.waitFor();

					}
				}

				Calendar timeStamp = Calendar.getInstance();
				SimpleDateFormat formatter = new SimpleDateFormat("yy_MM_dd_kk_mm_ss");
				String strTimeStamp = formatter.format(timeStamp.getTime());

				File sequenceFasta = new File(baseDir.getAbsolutePath() + "/" + name + "_" + strTimeStamp + ".fasta");
				BufferedWriter out = new BufferedWriter(new FileWriter(sequenceFasta));
				out.write(">" + name + "\n");
				out.write(seq + "\n");
				out.flush();
				out.close();
				
				//Set output file name to the current date/time
				File outFile = new File(baseDir.getAbsolutePath() + "/" + "Out_" + strTimeStamp + ".txt");

				//String BLASTPARAMS = (BlastArgs.isBlastPlus() ? " -evalue 1e-10" : "-e 1e-10");
				String blastCmd = BlastArgs.getBlastpCmd(sequenceFasta.getAbsolutePath(), combinedFasta.getAbsolutePath(),
						outFile.getAbsolutePath(), tabular);// + " " + BLASTPARAMS;
				
				System.out.println("Executing: " + blastCmd);//"blastall -p blastp " + BLASTPARAMS + format + " -i "+ sequenceFasta + " -d " + combinedFasta + " -o " + outFile);
				Process pBlastall = Runtime.getRuntime().exec(blastCmd);//"blastall -p blastp " + BLASTPARAMS + format + " -i "+ sequenceFasta + " -d " + combinedFasta + " -o " + outFile);
				pBlastall.waitFor();
				
				Vector<String> lines = new Vector<String> ();
				BufferedReader br = new BufferedReader(new FileReader(outFile));
				String line;
				
				while((line = br.readLine()) != null)
					lines.add(line);
						
				if(tabular)
					UserPrompt.displayInfoTable(pFrame, "Blast result for " + name, 
							lines.toArray(new String[0]), false, true);
				else
					UserPrompt.displayInfoMonoSpace(pFrame, "Blast result for " + name, 
							lines.toArray(new String[0]), false, true);
				
				sequenceFasta.delete();
				outFile.delete();
    		}
    		catch (Exception e) {
    			ErrorReport.prtReport(e, "Performing blast");
    		}
    }
	private boolean exportSequencesFasta(File target) {
		try {
			DBConn conn = pFrame.getDBConnection();
			int cntDS = conn.executeCount("SELECT COUNT(*) FROM assembly");
			String [] asmPfx = new String [cntDS+1]; 
			ResultSet rs = conn.executeQuery("SELECT ASMid, prefix FROM assembly");		
			while(rs.next()) 
				asmPfx[rs.getInt(1)] = rs.getString(2);
					
	        BufferedWriter pw = new BufferedWriter(new FileWriter(target));
	        // we are allowing duplicate sequence identifiers, so need to search by ASMid
	        for (int i=1; i<= cntDS;  i++) {
	        	rs = conn.executeQuery("SELECT UTstr, aaSeq FROM unitrans" +
	        			" WHERE ASMid=" + i);
				while(rs.next()) {
					String id = rs.getString(1);
					String seq = rs.getString(2);
					if (seq != null) {
						pw.write(">" + asmPfx[i] + "|" + id + "\n");
						pw.write(seq + "\n");
					}
				}
	        }
			rs.close();
			conn.close();	
			pw.flush();
			pw.close();
			return true;
		}
		catch(Exception e) {
			ErrorReport.prtReport(e, "Error generating fasta from database");
			return false;
		}
	}

    private int getTranslatedRow(int row) {
	    	if(row == theTable.getRowCount())
	    		return 0;
	    	if(row < 0)
	    		return theTable.getRowCount() - 1;
	    	return row;
    }
    
    private String [] getSelectedPOGMembers(boolean loadUnique) {
	    	String [] retVal = null;
	    	boolean clearSel = false;
	    	if(theTable.getSelectedRowCount() == 0) {
	    		theTable.selectAll();
	    		clearSel = true;
	    	}
	    	if(loadUnique) {
	    		Vector<String> results =  getSelectedUniqueColumnValues(theTableData.getColumnHeaderIndex("Seq ID"));
	    		retVal = results.toArray(new String[results.size()]);
	    	}
	    	else {
	    		retVal = getSelectedColumnValues(theTableData.getColumnHeaderIndex("Seq ID"));
	    	}
	
	    	if(clearSel)
	    		theTable.clearSelection();
	    	
	    	return retVal;
    }

    private Vector<String> getSelectedUniqueColumnValues(int colIndex) {
	    	Vector<String> theValues = new Vector<String> ();
	    	int [] sels = theTable.getSelectedRows();
	    	if(sels.length == 0) {
	    		theTable.selectAll();
	    		sels = theTable.getSelectedRows();
	    		theTable.clearSelection();
	    	}
	    	
	    	if(colIndex<0) return null;
	    	
	    	for(int x=0; x<sels.length; x++)
	    		if(!theValues.contains(theTableData.getValueAt(sels[x], colIndex)))
	    			theValues.add((String)theTableData.getValueAt(sels[x], colIndex));
	    	
	    	return theValues;
    }

    private String [] getSelectedColumnValues(int colIndex) {
	    	String [] theValues = null;
	    	int [] sels = theTable.getSelectedRows();
	    	if(sels.length == 0) {
	    		theTable.selectAll();
	    		sels = theTable.getSelectedRows();
	    		theTable.clearSelection();
	    	}
	    	
	    	if(colIndex<0) return null;
	    	
	    	theValues = new String[sels.length];
	    	for(int x=0; x<sels.length; x++)
	    		theValues[x] = ((String)theTableData.getValueAt(sels[x], colIndex));
	    	
	    	return theValues;
    }

    private String [] getUnitransQueryList(int row) {
		int IDidx = theTableData.getColumnHeaderIndex("POGID");
		int Nameidx = theTableData.getColumnHeaderIndex("Cluster ID");
		
		String sourceTable = "pog_members.PGid";
		
		String briefLabel = "";
		String fullLabel = "";
		String subquery = "";

		briefLabel = (String)theTableData.getValueAt(row, Nameidx);
		fullLabel = briefLabel;
		subquery = sourceTable + " = " + ((Long)theTableData.getValueAt(row, IDidx));
		
		briefLabel += ": ";				
		
		String [] retVal = new String[3];
		retVal[0] = briefLabel;
		retVal[1] = fullLabel;
		retVal[2] = subquery;

		return retVal;
    }
    
    private String [] getUnitransQueryList() {
    		// return the same numbers for IDidx and Nameidx everytime, e.g. 26 and 1
		int IDidx = theTableData.getColumnHeaderIndex("POGID");
		int Nameidx = theTableData.getColumnHeaderIndex("Cluster ID");
		String label = "Clusters";
		
		String sourceTable = "pog_members.PGid";
		
		String briefLabel = "";
		String fullLabel = "";
		String subquery = "";

		int [] sels = theTable.getSelectedRows();
		if(sels.length == 1) {
			briefLabel = (String)theTableData.getValueAt(sels[0], Nameidx);
			fullLabel = briefLabel;
			subquery = sourceTable + " = " + ((Long)theTableData.getValueAt(sels[0], IDidx));
		}
		else if(sels.length == 2) {
			briefLabel = (String)theTableData.getValueAt(sels[0], Nameidx);
			briefLabel += ", " +(String)theTableData.getValueAt(sels[1], Nameidx);							
			fullLabel = briefLabel;
			subquery = sourceTable + " = " + ((Long)theTableData.getValueAt(sels[0], IDidx));
			subquery += " OR " + sourceTable + " = " + ((Long)theTableData.getValueAt(sels[1], IDidx));
		}
		else {
			briefLabel = sels.length + " " + label;
			fullLabel = (String)theTableData.getValueAt(sels[0], Nameidx);
			subquery = sourceTable + " IN (" + ((Long)theTableData.getValueAt(sels[0], IDidx));
			for(int x=1; x<sels.length; x++) {
				fullLabel += ", " +(String)theTableData.getValueAt(sels[x], Nameidx);
				subquery += ", " + ((Long)theTableData.getValueAt(sels[x], IDidx));
			}
			subquery += ")";
		}
		briefLabel += ": ";				
		
		String [] retVal = new String[3];
		retVal[0] = briefLabel;
		retVal[1] = fullLabel;
		retVal[2] = subquery;

		return retVal;
    }
    
    private String getGroupQueryList(Long [] UTids) {
		String sourceTable = "pog_groups.PGid";
		
		String subquery = "";

		if(UTids.length == 1) {
			subquery = sourceTable + " = " + UTids[0];
		}
		else if(UTids.length == 2) {
			subquery = sourceTable + " = " + UTids[0];
			subquery += " OR " + sourceTable + " = " + UTids[1];
		}
		else {
			subquery = sourceTable + " IN (" + UTids[0];
			for(int x=1; x<UTids.length; x++) {
				subquery += ", " + UTids[x];
			}
			subquery += ")";
		}
		return subquery;    	
    }
    
    private MouseAdapter createStatusListener(final String messageIn, final String messageOut) {
	    	return new MouseAdapter() {
	    		public void mouseEntered(MouseEvent e) {
	    			pFrame.setStatus(messageIn);
	    		}
	    		public void mouseExited(MouseEvent e) {
	    			pFrame.setStatus(messageOut);
	    		}
	    	};
    }

    private String createTableString(String delim) {
    		StringBuilder retVal = new StringBuilder();
    		int nCol = theTable.getColumnCount();
    		
    		int rowNumColIndex = -1;
		for(int x=0; x<nCol-1; x++) {
			String val = theTable.getColumnName(x);
			if (val.equals(FieldData.ROWNUM)) rowNumColIndex = x;

			retVal.append(theTable.getColumnName(x));
			retVal.append(delim);
		}
		retVal.append(theTable.getColumnName(nCol-1));
		retVal.append("\n");
		
		for(int x=0; x<theTable.getRowCount(); x++) {
			for(int y=0; y<nCol; y++) {
				if (y == rowNumColIndex) {
					retVal.append("" + (x+1));
				}
				else { // CAS 6July15
					Object obj = theTable.getValueAt(x, y);
	                String val = (obj == null) ? "-" : convert(obj.toString()); 
					retVal.append(val);
				}
				if (y != nCol-1) retVal.append(delim);
			}
			retVal.append("\n");
			
			if(x % 5 == 0)
				pFrame.setStatus("Copying table: " + ((int)((((double)x)/theTable.getRowCount()) * 100)) + "%");
		}
		pFrame.setStatus("");

    		return retVal.toString();
    }
    
    private BufferedWriter getWriter(String dName) {
    		BufferedWriter out = null;
    		try {
			JFileChooser fc = new JFileChooser(System.getProperty("user.dir"));	
			fc.setSelectedFile(new File(dName));
			if(fc.showSaveDialog(pFrame) != JFileChooser.APPROVE_OPTION) return out;
			if(fc.getSelectedFile() == null) return out;
			
			File f = fc.getSelectedFile();
			String filePath = f.getPath();
			boolean append=false;
			
			if (f.exists()) {
				Object[] options = {"Cancel", "Overwrite", "Append"};
				int n = JOptionPane.showOptionDialog(this,
				    "The file exists",
				    "File exists",
				    JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE,
				    null, options, options[2]);
				if (n>0) {
					if (n==1) append=false;
					else append=true;
					System.out.println(" Write file " + filePath);
					return new BufferedWriter(new FileWriter(fc.getSelectedFile(), append));
				}
			}
			return null;
    		} catch (Exception e) {
    			ErrorReport.prtReport(e, "Error: cannot write file");
    			return out;
    		}
    }
    private void exportTableTab(String delim) {
    		try {
			BufferedWriter out = getWriter("Table.csv");
			if (out==null) return; // user cancels
			
			// print header line
			String val = "";
			int rowNumColIndex = -1;
			for(int x=0; x<theTable.getColumnCount()-1; x++) {
				val = theTable.getColumnName(x);
				if (val.equals(FieldData.ROWNUM)) rowNumColIndex = x;
				val = val.replaceAll("\\s", "");
				out.write(val + delim);
			}
			val = theTable.getColumnName(theTable.getColumnCount()-1).replaceAll("\\s", "");
			out.write(val + "\n");
			out.flush();
			
			// print table
			for(int x=0; x<theTable.getRowCount(); x++) {
				for(int y=0; y<theTable.getColumnCount()-1; y++) {
					if (y == rowNumColIndex) out.write((x+1) + delim);
					else {
						Object obj = theTable.getValueAt(x, y);
		                val = (obj == null) ? "-" : convert(obj.toString()); 
						out.write(val + delim);
					}
				}
				Object obj = theTable.getValueAt(x, theTable.getColumnCount()-1);
                val = (obj == null ? "-" : convert(obj.toString()));
				out.write(val + "\n");
				out.flush();
			}
			System.err.println("Complete writing  - " + theTable.getRowCount() + " lines");
			out.close();
		} catch(Exception e) {
			ErrorReport.reportError(e, "Error saving file");
		} catch(Error e) {
			ErrorReport.reportFatalError(e, "Fatal error saving file", pFrame);
		}
    }
    
    private void exportClusterSeqTab() {
    		try { 
    			BufferedWriter out = getWriter("Members.csv");
    			if (out==null) return;
    			
    			// create SQL column headings and write to file
    			DBConn dbc = pFrame.getDBConnection();
    			DBinfo dbi = new DBinfo(dbc, false);		
			String [] lib = dbi.getSeqLib();
			String [] de = dbi.getSeqDE();
			String seqColsSQL = "";
			seqColsSQL = "unitrans.UTstr "; 
			out.write("Cluster" + "\t" + "Sequence");
			for (int i=0; i<lib.length; i++) {
				seqColsSQL += ", unitrans." + Globals.PRE_LIB + lib[i];
				out.write("\t" + lib[i]);
			}
			for (int i=0; i<de.length; i++) {
				seqColsSQL += ", unitrans." + Globals.PRE_DE + de[i];
				out.write("\t" + de[i]);
			}
			out.write("\n"); out.flush();
			int seqColsNum = 1 + lib.length + de.length;
			
			// which column contains the clusterID
			int cIDindex = -1;	
			for(int x=0; x<theTable.getColumnCount()-1; x++) {
				String val = theTable.getColumnName(x);
				if (val.equals(FieldData.CLUSTERID)) cIDindex = x;
			}
			if (cIDindex == -1 ) {
				System.err.println("Cluster ID is not visable");
				JOptionPane.showMessageDialog(pFrame, "Cluster ID must be a column in table\nSelect it from columns", 
						"Error",  JOptionPane.PLAIN_MESSAGE);
				return;
			}
			
			// build vector of clusters
			String [] PGstr = new String [theTable.getRowCount()];
			for(int i=0; i<theTable.getRowCount(); i++) {
				PGstr[i] = (String) theTable.getValueAt(i, cIDindex);
			}
			
			// query data and output
			int cnt=0;
			for (int i=0; i<PGstr.length; i++) {
				ResultSet rs = dbc.executeQuery("SELECT " + seqColsSQL + 
						" FROM unitrans JOIN pog_members " +
						" WHERE pog_members.PGstr= '" + PGstr[i] + "' AND unitrans.UTid=pog_members.UTid");
				while (rs.next()) {
					out.write(PGstr[i]);
					for (int j=0; j<seqColsNum; j++) {
						String s = rs.getString(j+1).trim();
						out.write("\t" + convert(s));
					}
					out.write("\n");
					out.flush();
					cnt++;
				}
				System.err.print("   Wrote cluster " + PGstr[i] + "\r");
				rs.close();
			}
			out.close();
			System.out.println("\nComplete writing member information for clusters -- " + cnt + " lines");
			
		} catch(Exception e) {
			ErrorReport.reportError(e, "Error saving file");
		}
    }
    // CAS 7July15 -- it was not recognizing the string -2 and -2.0; had to convert to make it work
    private String convert(String s) {
    		s = s.trim();
    		if (s==null || s=="") return "-";
    		try {
    			int n = Integer.parseInt(s);
    			if (n==-2) return "-";
    			else return s;
    		}
    		catch (Exception e) {}
    		try {
    			double n = Double.parseDouble(s);
    			if (n==-2.0) return "-";
    			else return s;
    		}
    		catch (Exception e) {}
    		return s;
    }
    private JPanel createTableSummaryPanel() {
	    	JPanel thePanel = new JPanel();
	    	thePanel.setLayout(new BoxLayout(thePanel, BoxLayout.LINE_AXIS));
	    	thePanel.setBackground(pFrame.getSettings().getFrameSettings().getBGColor());
	    	thePanel.setAlignmentX(Component.LEFT_ALIGNMENT);
	
	    	lblSummary = new JLabel(strQuerySummary);
	    	lblSummary.setMaximumSize(lblSummary.getPreferredSize());
	    	lblSummary.setAlignmentX(Component.LEFT_ALIGNMENT);
	    	lblSummary.setFont(getFont());
	    	
	    	JLabel header =new JLabel("Summary: ");
	    	
	    	thePanel.add(header);
	    	thePanel.add(lblSummary);
	    	
	    	return thePanel;
    }
    
    private JPanel createTableStatusPanel() {
	    	JPanel thePanel = new JPanel();
	    	thePanel.setLayout(new BoxLayout(thePanel, BoxLayout.LINE_AXIS));
	    	thePanel.setBackground(pFrame.getSettings().getFrameSettings().getBGColor());
	    	thePanel.setAlignmentX(Component.LEFT_ALIGNMENT);
	
	    	tableType = new JTextField(20);
	    	tableType.setBackground(pFrame.getSettings().getFrameSettings().getBGColor());
	    	tableType.setBorder(BorderFactory.createEmptyBorder());
	    	tableType.setEditable(false);
	    	Font f = tableType.getFont();
	    	tableType.setFont(new Font(f.getFontName(),Font.BOLD, f.getSize()));
	    	tableType.setMaximumSize(tableType.getPreferredSize());
	    	tableType.setAlignmentX(LEFT_ALIGNMENT);
	
	    	rowCount = new JTextField(30);
	    	rowCount.setBackground(pFrame.getSettings().getFrameSettings().getBGColor());
	    	rowCount.setBorder(BorderFactory.createEmptyBorder());
	    	rowCount.setEditable(false);
	    	rowCount.setMaximumSize(rowCount.getPreferredSize());
	    	rowCount.setAlignmentX(LEFT_ALIGNMENT);
	    	thePanel.add(tableType);
	    	thePanel.add(rowCount);
	    	thePanel.setMaximumSize(thePanel.getPreferredSize());
	    	
	    	return thePanel;
    }
     
    private void updateTableStatus() {
	    	if(nQueryMode == MODE_POG) 				{ tableType.setText("Cluster View");
	    	} else if(nQueryMode == MODE_POG_MEMBER) {tableType.setText("Member View");
	    	} else if(nQueryMode == MODE_UNITRANS) 	{tableType.setText("Sequence View");
	    	}
    }
    
    public void setCacheFilename(String POGName) { strCacheFileName = POGName; }
    public String getCacheFilename() { return strCacheFileName; }

    private static int nTableID = 0;

    private int nParentRow = -1; 
    private SortTable theTable = null;
    private TableData theTableData = null;
    private JScrollPane sPane = null;
    private int nQueryMode = -1;
    private JTextField rowCount = null;
    private JTextField tableType = null;
    private JTextField loadStatus = null;
    private JTextField txtStatus = null;
    private JLabel lblSummary = null;
    //Function buttons
    private JButton btnCopy = null;
    private JButton btnCopyTable = null;
    private JButton btnExport = null;
    private JButton btnClearSelection = null;
    private JButton btnBlast = null;
    private JButton btnViewDetails = null;
    private JButton btnViewGroups = null;
    private JButton btnHelp = null;
    //Next/Previous
    private JButton btnNextRow = null;
    private JButton btnPrevRow = null;
    
    private JButton btnShowUnitrans = null;
    private JButton btnViewPairwiseAlignment = null;
    private JButton btnViewMultiAlignment = null;
    private JButton btnImportMultiAlign = null;
    
    private ActionListener dblClick = null;
    private ActionListener sngClick = null;
    private ListSelectionListener selListener = null;
    private String strSubQuery = "";
    private String strTable = "";
    private String strQuerySummary = null;
	private String strName = "";
    private Thread buildThread = null;
    private String strCacheFileName = null;
    
    private ListDataPanel theParentList = null;
    private JCheckBox [] chkFields = null;
    private ActionListener colSelectChange = null;
    
	private JPanel tableButtonPanel = null;
	private JPanel tableStatusPanel = null;
	private JPanel fieldSelectPanel = null;
	private JPanel tableSummaryPanel = null;
	
	private JButton showColumnSelect = null;
	
	private ViewerFrame pFrame = null;
        
	//Sort listener for columns 
    private class SortHeader extends JTableHeader {
		private static final long serialVersionUID = -2417422687456468175L;
	    	public SortHeader(TableColumnModel model) {
	    		super(model);
	    		bColumnAscending = new boolean[model.getColumnCount()];
	    		for(int x=0; x<bColumnAscending.length; x++)
	    			bColumnAscending[x] = true;
	    		
	    		addMouseListener(new MouseAdapter() {
	            	public void mouseClicked(MouseEvent evt) 
	            	{ 
	            		try {
		            		SortTable table = (SortTable)((JTableHeader)evt.getSource()).getTable(); 
		            		TableColumnModel colModel = table.getColumnModel(); 
		            		int vColIndex = colModel.getColumnIndexAtX(evt.getX()); 
		            		int mColIndex = table.convertColumnIndexToModel(vColIndex);
		            		if(SwingUtilities.isLeftMouseButton(evt)) {
		            			table.sortAtColumn(mColIndex, bColumnAscending[mColIndex]);
		            			bColumnAscending[mColIndex] = !bColumnAscending[mColIndex];
		            		}
		            		else {
		            			bColumnAscending[mColIndex] = false;
		            			table.sortAtColumn(mColIndex, false);
		            		}
	            		}
		            	catch (Exception e) { ErrorReport.prtReport(e, "Internal mouse click error");}
	             }   
	    		});
	    	}
	    	
	    	private boolean [] bColumnAscending = null;
    }
    
    public class MultiLineHeaderRenderer extends JList implements TableCellRenderer {
		private static final long serialVersionUID = 3118619652018757230L;

		public MultiLineHeaderRenderer() {
    	    setOpaque(true);
    	    setBorder(BorderFactory.createLineBorder(Color.BLACK));
    	    setBackground(Globals.BGCOLOR);
    	    ListCellRenderer renderer = getCellRenderer();
    	    ((JLabel)renderer).setHorizontalAlignment(JLabel.CENTER);
    	    setCellRenderer(renderer);
    	  }
    	 
    	  public Component getTableCellRendererComponent(JTable table, Object value,
    	                   boolean isSelected, boolean hasFocus, int row, int column) {
    	    setFont(table.getFont());
    	    String str = (value == null) ? "" : value.toString();
    	    BufferedReader br = new BufferedReader(new StringReader(str));
    	    String line;
    	    Vector<String> v = new Vector<String>();
    	    try {
    	      while ((line = br.readLine()) != null) {
    	        v.addElement(line);
    	      }
    	    } catch (Exception e) {
    	      ErrorReport.reportError(e, "Error rendering table cells");
    	    } catch(Error e) {
    	    		ErrorReport.reportFatalError(e, "Fatal error rendering table cells", null);
    	    }
    	    setListData(v);
    	    return this;
    	  }
    	}
}
